#include "agile_modbus_rtu_master.hpp"
#include <iomanip>
int agile_modbus_rtu_master_simple::_read_slave(read_write_type type, uint8_t slave_id, uint16_t addr, uint32_t num, std::vector<uint16_t> &data, bool is_force_muti)
{
    int flag = 0;
    uint8_t u8_buf[num];
    int send_len, recv_len, rc;
    if (data.size() < num || init_flag < 0)
    {
        LOG(ERROR) << "❌ rtu_master_simple " << name << " init is not success,please init first";
        return -1;
    }
    std::lock_guard<std::mutex> lock(ctx_mutex);

    agile_modbus_set_slave(ctx, slave_id);

    switch (type)
    {
    case COILS:
        send_len = agile_modbus_serialize_read_bits(ctx, addr, num);
        send_callback(ctx->send_buf, send_len);
        recv_len = recv_callback(ctx->read_buf);
        if (recv_len < 0)
        {
            LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << "Read receive error";
            if (recv_len == -2)
            {
                LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << "Read receive out of buff size";
            }
            flag = -1;
            break;
        }
        else if (recv_len == 0)
        {
            LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Read receive timeout";
            flag = -1;
            break;
        }

        rc = agile_modbus_deserialize_read_bits(ctx, recv_len, u8_buf);
        if (rc < 0)
        {
            LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Read deserialize failed.";
            flag = -1;
            if (rc != -1)
            {
                LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Error code: " << -128 - rc;
                flag = -1;
            }
            break;
        }
        data.assign(u8_buf, u8_buf + num);
        break;
    case DISCRETE_INPUTS:
        send_len = agile_modbus_serialize_read_input_bits(ctx, addr, num);
        send_callback(ctx->send_buf, send_len);
        recv_len = recv_callback(ctx->read_buf);
        if (recv_len < 0)
        {
            LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Read receive error";
            if (recv_len == -2)
            {
                LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << "Read receive out of buff size";
            }
            flag = -1;
            break;
        }
        else if (recv_len == 0)
        {
            LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Read receive timeout";
            flag = -1;
            break;
        }

        rc = agile_modbus_deserialize_read_input_bits(ctx, recv_len, u8_buf);
        if (rc < 0)
        {
            LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Read deserialize failed.";
            flag = -1;
            if (rc != -1)
            {
                LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Error code: " << -128 - rc;
                flag = -1;
            }
            break;
        }
        data.assign(u8_buf, u8_buf + num);
        break;
    case HOLDING_REGISTERS:
        send_len = agile_modbus_serialize_read_registers(ctx, addr, num);
        // LOG(INFO) << "rtu_master_simple " <<name << " slave id: "<< (int)slave_id << " Read Holding Registers send_len:" << send_len << " read num: "<< num;
        // LOG(INFO) << "rtu_master_simple " <<name << " slave id: "<< (int)slave_id << " Read Holding Registers send_buf:" << std::hex << std::setfill('0') << std::setw(2) << addr;
        send_callback(ctx->send_buf, send_len);
        recv_len = recv_callback(ctx->read_buf);
        if (recv_len < 0)
        {
            LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Read receive error";
            if (recv_len == -2)
            {
                LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << "Read receive out of buff size";
            }
            flag = -1;
            break;
        }
        else if (recv_len == 0)
        {
            LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Read receive timeout";
            flag = -1;
            break;
        }
        rc = agile_modbus_deserialize_read_registers(ctx, recv_len, data.data());
        if (rc < 0)
        {
            LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Read deserialize failed.";
            flag = -1;
            if (rc != -1)
            {
                LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Error code: " << -128 - rc;
                flag = -1;
            }
            // for(int i = 0; i < recv_len; i++)
            // {
            //     LOG(INFO) << "❌ rtu_master_simple " <<name << " slave id: "<< (int)slave_id << " Read receive data:" << std::hex << (int)ctx->read_buf[i];
            // }
            break;
        }
        break;
    case INPUT_REGISTERS:
        send_len = agile_modbus_serialize_read_input_registers(ctx, addr, num);
        send_callback(ctx->send_buf, send_len);
        recv_len = recv_callback(ctx->read_buf);
        if (recv_len < 0)
        {
            LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Read receive error";
            if (recv_len == -2)
            {
                LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << "Read receive out of buff size";
            }
            flag = -1;
            break;
        }
        else if (recv_len == 0)
        {
            LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Read receive timeout";
            flag = -1;
            break;
        }

        rc = agile_modbus_deserialize_read_input_registers(ctx, recv_len, data.data());
        if (rc < 0)
        {
            LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Read deserialize failed.";
            flag = -1;
            if (rc != -1)
            {
                LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Error code: " << -128 - rc;
                flag = -1;
            }
            break;
        }
        break;
    default:
        LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " read_slave_single type error";
        flag = -1;
        break;
    }
    // if (flag == 0)
    //     LOG(INFO) << "✅ rtu_master_simple " <<name << " slave id: "<< (int)slave_id << " read slave ok";
    return flag;
}

int agile_modbus_rtu_master_simple::_write_slave(read_write_type type, uint8_t slave_id, uint16_t addr, uint32_t num, std::vector<uint16_t> &data, bool is_force_muti)
{
    int flag = 0, _i;
    uint8_t u8_buf[num];
    int send_len, recv_len, rc;
    if (data.size() < 1 || init_flag < 0)
    {
        LOG(ERROR) << "❌ rtu_master_simple " << name << " init is not success,please init first";
        return -1;
    }
    std::lock_guard<std::mutex> lock(ctx_mutex);

    agile_modbus_set_slave(ctx, slave_id);

    switch (type)
    {
    case COILS:
        for (_i = 0; _i < num; _i++)
        {
            u8_buf[_i] = data[_i] > 0 ? 1 : 0;
        }

        if (num == 1 && is_force_muti == false)
            send_len = agile_modbus_serialize_write_bit(ctx, addr, u8_buf[0]);
        else
            send_len = agile_modbus_serialize_write_bits(ctx, addr, num, u8_buf);
        send_callback(ctx->send_buf, send_len);
        if (slave_id == 0)
            break;
        recv_len = recv_callback(ctx->read_buf);
        if (recv_len < 0)
        {
            LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Write receive error";
            if (recv_len == -2)
            {
                LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << "Write receive out of buff size";
            }
            flag = -1;
            break;
        }
        else if (recv_len == 0)
        {
            LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Write receive timeout";
            flag = -1;
            break;
        }
        if (num == 1 && is_force_muti == false)
            rc = agile_modbus_deserialize_write_bit(ctx, recv_len);
        else
            rc = agile_modbus_deserialize_write_bits(ctx, recv_len);
        if (rc < 0)
        {
            LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Write deserialize failed.";
            flag = -1;
            if (rc != -1)
            {
                LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Error code: " << -128 - rc;
                flag = -1;
            }
            break;
        }
        break;
    case DISCRETE_INPUTS:
        LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Discrete inputs can't be written";
        flag = -1;
        break;
    case HOLDING_REGISTERS:
        if (num == 1 && is_force_muti == false)
            send_len = agile_modbus_serialize_write_register(ctx, addr, data.data()[0]);
        else
            send_len = agile_modbus_serialize_write_registers(ctx, addr, num, data.data());

        send_callback(ctx->send_buf, send_len);
        if (slave_id == 0)
            break;
        // for(int i = 0; i < send_len; i++)
        // {
        //     LOG(ERROR) << "---- rtu_master_simple " <<name << " slave id: "<< (int)slave_id << "ctx send_buf ["<<i<<"] "<< std::hex <<(int)ctx->send_buf[i];
        // }

        recv_len = recv_callback(ctx->read_buf);
        if (recv_len < 0)
        {
            LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Write receive error";
            if (recv_len == -2)
            {
                LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << "Write receive out of buff size";
            }
            flag = -1;
            break;
        }
        else if (recv_len == 0)
        {
            LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Write receive timeout";
            flag = -1;
            break;
        }

        if (num == 1 && is_force_muti == false)
            rc = agile_modbus_deserialize_write_register(ctx, recv_len);
        else
            rc = agile_modbus_deserialize_write_registers(ctx, recv_len);
        if (rc < 0)
        {
            LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Write deserialize failed.";

            // for(int i = 0; i < recv_len; i++)
            // {
            //     LOG(ERROR) << "---- rtu_master_simple " <<name << " slave id: "<< (int)slave_id << "ctx buff ["<<i<<"] "<< std::hex <<(int)ctx->read_buf[i];
            // }

            flag = -1;
            if (rc != -1)
            {
                LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Error code: " << -128 - rc;
                flag = -1;
            }
            break;
        }
        break;
    case INPUT_REGISTERS:
        LOG(WARNING) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " Input registers can't be written";
        flag = -1;
        break;
    default:
        LOG(ERROR) << "❌ rtu_master_simple " << name << " slave id: " << (int)slave_id << " read_slave_single type error";
        flag = -1;
        break;
    }
    // if (flag == 0)
    //     LOG(INFO) << "✅ rtu_master_simple " <<name << " slave id: "<< (int)slave_id << " write slave ok";
    return flag;
}

agile_modbus_rtu_master_simple::agile_modbus_rtu_master_simple(std::string &name_,
                                                               std::function<int(uint8_t *data, uint32_t send_size)> send_callback_,
                                                               std::function<int(uint8_t *data)> recv_callback_) : send_callback(send_callback_),
                                                                                                                   recv_callback(recv_callback_),
                                                                                                                   name(name_),
                                                                                                                   ctx(&ctx_rtu._ctx),
                                                                                                                   ctx_max_buf_len(MAX_ADU_LENGTH)
{
    init_flag = init();

    if (init_flag < 0)
    {
        // TODO: log
        LOG(ERROR) << "❌ rtu_master_simple " << name << " init failed";
        return;
    }

    if (!send_callback)
    {
        LOG(ERROR) << "❌ rtu_master_simple " << name << " send_callback is null";
        init_flag = -1;
        return;
    }

    if (!recv_callback)
    {
        LOG(ERROR) << "❌ rtu_master_simple " << name << " recv_callback is null";
        init_flag = -1;
        return;
    }

    LOG(INFO) << "✅ rtu_master_simple " << name << " init success";
}

agile_modbus_rtu_master_simple::~agile_modbus_rtu_master_simple()
{
}

int agile_modbus_rtu_master_simple::init()
{
    int flag = 0;
    flag = agile_modbus_rtu_init(&ctx_rtu, ctx_send_buf, MAX_ADU_LENGTH, ctx_read_buf, MAX_ADU_LENGTH);
    return flag;
}

int agile_modbus_rtu_master_simple::read_coil_single(uint8_t slave_id, uint16_t addr, std::vector<uint16_t> &data)
{
    return _read_slave(COILS, slave_id, addr, 1, data);
}

int agile_modbus_rtu_master_simple::read_discrete_input_single(uint8_t slave_id, uint16_t addr, std::vector<uint16_t> &data)
{
    return _read_slave(DISCRETE_INPUTS, slave_id, 1, addr, data);
}

int agile_modbus_rtu_master_simple::read_input_register_single(uint8_t slave_id, uint16_t addr, std::vector<uint16_t> &data)
{
    return _read_slave(INPUT_REGISTERS, slave_id, 1, addr, data);
}

int agile_modbus_rtu_master_simple::read_holding_register_single(uint8_t slave_id, uint16_t addr, std::vector<uint16_t> &data)
{
    return _read_slave(HOLDING_REGISTERS, slave_id, 1, addr, data);
}

int agile_modbus_rtu_master_simple::write_coil_single(uint8_t slave_id, uint16_t addr, std::vector<uint16_t> &data)
{
    return _write_slave(COILS, slave_id, addr, 1, data);
}

int agile_modbus_rtu_master_simple::write_holding_register_single(uint8_t slave_id, uint16_t addr, std::vector<uint16_t> &data)
{
    return _write_slave(HOLDING_REGISTERS, slave_id, addr, 1, data);
}

int agile_modbus_rtu_master_simple::read_coil_multiple(uint8_t slave_id, uint16_t addr, std::vector<uint16_t> &data)
{
    return _read_slave(COILS, slave_id, addr, data.size(), data, true);
}

int agile_modbus_rtu_master_simple::read_discrete_input_multiple(uint8_t slave_id, uint16_t addr, std::vector<uint16_t> &data)
{
    return _read_slave(DISCRETE_INPUTS, slave_id, addr, data.size(), data, true);
}

int agile_modbus_rtu_master_simple::read_holding_register_multiple(uint8_t slave_id, uint16_t addr, std::vector<uint16_t> &data)
{
    return _read_slave(HOLDING_REGISTERS, slave_id, addr, data.size(), data, true);
}

int agile_modbus_rtu_master_simple::read_input_register_multiple(uint8_t slave_id, uint16_t addr, std::vector<uint16_t> &data)
{
    return _read_slave(INPUT_REGISTERS, slave_id, addr, data.size(), data, true);
}

int agile_modbus_rtu_master_simple::write_coil_multiple(uint8_t slave_id, uint16_t addr, std::vector<uint16_t> &data)
{
    return _write_slave(COILS, slave_id, addr, data.size(), data, true);
}

int agile_modbus_rtu_master_simple::write_holding_register_multiple(uint8_t slave_id, uint16_t addr, std::vector<uint16_t> &data)
{
    return _write_slave(HOLDING_REGISTERS, slave_id, addr, data.size(), data, true);
}

uint32_t agile_modbus_rtu_master_simple::read_max_buffer_size()
{
    return ctx_max_buf_len;
}